สำรวจ Next.js Parallel Routes: คู่มือฉบับสมบูรณ์สำหรับการสร้างเลย์เอาต์เพจที่ไดนามิกและยืดหยุ่นด้วยส่วนต่างๆ ที่เป็นอิสระต่อกัน เรียนรู้การนำไปใช้ ประโยชน์ และแนวทางปฏิบัติที่ดีที่สุด
Next.js Parallel Routes: การสร้างเลย์เอาต์เพจแบบไดนามิก
Next.js ซึ่งเป็นเฟรมเวิร์ก React ชั้นนำ มีการพัฒนาอย่างต่อเนื่องเพื่อมอบเครื่องมืออันทรงพลังแก่นักพัฒนาสำหรับการสร้างเว็บแอปพลิเคชันสมัยใหม่ หนึ่งในฟีเจอร์ที่น่าตื่นเต้นที่สุดที่เปิดตัวในเวอร์ชันล่าสุดคือ Parallel Routes ฟีเจอร์นี้ช่วยให้คุณสามารถเรนเดอร์ส่วนต่างๆ ที่เป็นอิสระต่อกันหลายส่วนภายในเลย์เอาต์ของหน้าเดียวกันได้ ซึ่งมอบความยืดหยุ่นและการควบคุมโครงสร้างของแอปพลิเคชันและประสบการณ์ผู้ใช้ได้อย่างที่ไม่เคยมีมาก่อน
Parallel Routes คืออะไร?
โดยปกติแล้ว route ใน Next.js จะสอดคล้องกับ page component เพียงตัวเดียว เมื่อคุณไปยัง route อื่น ทั้งหน้าจะถูกเรนเดอร์ใหม่ Parallel Routes ทำลายกระบวนทัศน์นี้โดยทำให้คุณสามารถเรนเดอร์ component หลายตัว พร้อมกัน ภายในเลย์เอาต์เดียวกัน โดยแต่ละตัวจะถูกจัดการโดย route segment ที่เป็นอิสระของตัวเอง ลองนึกภาพว่ามันคือการแบ่งหน้าของคุณออกเป็นส่วนต่างๆ ที่ชัดเจน โดยแต่ละส่วนมี URL และ lifecycle เป็นของตัวเอง ทั้งหมดอยู่ร่วมกันบนหน้าจอเดียว
สิ่งนี้ปลดล็อกความเป็นไปได้มากมายสำหรับการสร้างส่วนติดต่อผู้ใช้ที่ซับซ้อนและไดนามิกมากขึ้น ตัวอย่างเช่น คุณสามารถใช้ parallel routes เพื่อ:
- แสดงแถบนำทางที่คงที่ควบคู่ไปกับเนื้อหาหลัก
- สร้างหน้าต่างโมดัลหรือแถบด้านข้างโดยไม่ส่งผลกระทบต่อการทำงานหลักของหน้า
- สร้างแดชบอร์ดที่มีวิดเจ็ตอิสระที่สามารถโหลดและอัปเดตแยกกันได้
- ทดสอบ A/B test ของ component เวอร์ชันต่างๆ โดยไม่ส่งผลกระทบต่อโครงสร้างโดยรวมของหน้า
ทำความเข้าใจแนวคิด: Slots
แนวคิดหลักเบื้องหลัง Parallel Routes คือแนวคิดของ "slots" slot คือพื้นที่ที่มีชื่ออยู่ในเลย์เอาต์ของคุณซึ่งเป็นที่สำหรับเรนเดอร์ route segment ที่เจาะจง คุณกำหนด slots เหล่านี้ในไดเรกทอรี app ของคุณโดยใช้สัญลักษณ์ @ ตามด้วยชื่อ slot ตัวอย่างเช่น @sidebar แทน slot ที่ชื่อว่า "sidebar"
จากนั้นแต่ละ slot จะสามารถเชื่อมโยงกับ route segment ได้ เมื่อผู้ใช้ไปยัง route ที่เจาะจง Next.js จะเรนเดอร์ component ที่เชื่อมโยงกับ route segment นั้นลงใน slot ที่สอดคล้องกันในเลย์เอาต์
การนำไปใช้งาน: ตัวอย่างที่ปฏิบัติได้จริง
ลองมาดูตัวอย่างการทำงานของ Parallel Routes กัน สมมติว่าคุณกำลังสร้างแอปพลิเคชันอีคอมเมิร์ซ และคุณต้องการแสดงหน้ารายละเอียดสินค้าพร้อมกับแถบด้านข้างของตะกร้าสินค้าที่แสดงผลอยู่ตลอดเวลา
1. โครงสร้างไดเรกทอรี
ขั้นแรก เรามากำหนดโครงสร้างไดเรกทอรีสำหรับแอปพลิเคชันของเรากัน:
app/
product/
[id]/
@cart/
page.js // คอมโพเนนต์ตะกร้าสินค้า
page.js // คอมโพเนนต์รายละเอียดสินค้า
layout.js // เลย์เอาต์สินค้า
layout.js // Root layout
นี่คือคำอธิบายของแต่ละไฟล์:
- app/layout.js: Root layout สำหรับทั้งแอปพลิเคชัน
- app/product/[id]/layout.js: เลย์เอาต์เฉพาะสำหรับหน้ารายละเอียดสินค้า ที่นี่คือที่ที่เราจะกำหนด slots ของเรา
- app/product/[id]/page.js: คอมโพเนนต์หลักของรายละเอียดสินค้า
- app/product/[id]/@cart/page.js: คอมโพเนนต์ตะกร้าสินค้า ซึ่งจะถูกเรนเดอร์ใน
@cartslot
2. Root Layout (app/layout.js)
โดยทั่วไป Root layout จะมีองค์ประกอบที่ใช้ร่วมกันทั่วทั้งแอปพลิเคชัน เช่น ส่วนหัวและส่วนท้าย
// app/layout.js
export default function RootLayout({ children }) {
return (
My E-commerce App
{children}
);
}
3. Product Layout (app/product/[id]/layout.js)
นี่คือส่วนสำคัญที่เรากำหนด slots ของเรา เราจะได้รับคอมโพเนนต์สำหรับหน้าสินค้าหลักและตะกร้าสินค้าเป็น props ซึ่งสอดคล้องกับ page.js และ @cart/page.js ตามลำดับ
// app/product/[id]/layout.js
export default function ProductLayout({ children, cart }) {
return (
{children}
);
}
ในตัวอย่างนี้ เราใช้เลย์เอาต์ flexbox แบบง่ายๆ เพื่อวางเนื้อหาสินค้าหลักและแถบด้านข้างของตะกร้าสินค้าไว้ข้างกัน prop children จะมีผลลัพธ์ที่เรนเดอร์จาก app/product/[id]/page.js และ prop cart จะมีผลลัพธ์ที่เรนเดอร์จาก app/product/[id]/@cart/page.js
4. หน้ารายละเอียดสินค้า (app/product/[id]/page.js)
นี่คือหน้า dynamic route มาตรฐานที่แสดงรายละเอียดสินค้าตามพารามิเตอร์ id
// app/product/[id]/page.js
export default async function ProductDetails({ params }) {
const { id } = params;
// ดึงข้อมูลสินค้าตาม ID
const product = await fetchProduct(id);
return (
Product Details
{product.name}
{product.description}
Price: ${product.price}
);
}
async function fetchProduct(id) {
// แทนที่ด้วยโลจิกการดึงข้อมูลจริงของคุณ
return new Promise(resolve => setTimeout(() => {
resolve({ id, name: `Product ${id}`, description: `Description of Product ${id}`, price: 99.99 });
}, 500));
}
5. คอมโพเนนต์ตะกร้าสินค้า (app/product/[id]/@cart/page.js)
คอมโพเนนต์นี้แสดงถึงตะกร้าสินค้า ซึ่งจะถูกเรนเดอร์ใน @cart slot
// app/product/[id]/@cart/page.js
export default function ShoppingCart() {
return (
Shopping Cart
Items in cart: 3
);
}
คำอธิบาย
เมื่อผู้ใช้ไปที่ /product/123 Next.js จะ:
- เรนเดอร์ root layout (
app/layout.js) - เรนเดอร์ product layout (
app/product/[id]/layout.js) - ภายใน product layout จะเรนเดอร์คอมโพเนนต์รายละเอียดสินค้า (
app/product/[id]/page.js) ลงใน propchildren - ในขณะเดียวกัน ก็จะเรนเดอร์คอมโพเนนต์ตะกร้าสินค้า (
app/product/[id]/@cart/page.js) ลงใน propcart
ผลลัพธ์ที่ได้คือหน้ารายละเอียดสินค้าพร้อมแถบด้านข้างตะกร้าสินค้าที่แสดงผลอยู่ตลอดเวลา ทั้งหมดถูกเรนเดอร์ภายในเลย์เอาต์เดียว
ประโยชน์ของการใช้ Parallel Routes
- ปรับปรุงประสบการณ์ผู้ใช้: สร้างส่วนติดต่อผู้ใช้ที่มีการโต้ตอบและน่าดึงดูดยิ่งขึ้นด้วยองค์ประกอบที่คงที่และส่วนต่างๆ ที่เป็นไดนามิก
- เพิ่มความสามารถในการนำโค้ดกลับมาใช้ใหม่: แบ่งปันคอมโพเนนต์และเลย์เอาต์ระหว่าง routes ต่างๆ ได้ง่ายขึ้น
- ประสิทธิภาพที่เพิ่มขึ้น: โหลดและอัปเดตส่วนต่างๆ ของหน้าได้อย่างอิสระ ลดความจำเป็นในการเรนเดอร์หน้าใหม่ทั้งหมด
- การพัฒนาที่ง่ายขึ้น: จัดการเลย์เอาต์และการโต้ตอบที่ซับซ้อนด้วยโครงสร้างที่เป็นโมดูลและเป็นระเบียบมากขึ้น
- ความสามารถในการทำ A/B Testing: ทดสอบรูปแบบต่างๆ ของส่วนหน้าเฉพาะได้อย่างง่ายดายโดยไม่ส่งผลกระทบต่อทั้งหน้า
ข้อควรพิจารณาและแนวทางปฏิบัติที่ดีที่สุด
- ความขัดแย้งของ Route: ระมัดระวังเพื่อหลีกเลี่ยงความขัดแย้งของ route ระหว่าง parallel routes แต่ละ route segment ควรมีจุดประสงค์ที่ไม่ซ้ำกันและไม่ทับซ้อนกับ segment อื่นๆ
- ความซับซ้อนของเลย์เอาต์: แม้ว่า parallel routes จะให้ความยืดหยุ่น แต่การใช้งานมากเกินไปอาจนำไปสู่เลย์เอาต์ที่ซับซ้อนและดูแลรักษายาก พยายามสร้างสมดุลระหว่างความยืดหยุ่นและความเรียบง่าย
- ผลกระทบต่อ SEO: พิจารณาผลกระทบต่อ SEO ของการใช้ parallel routes โดยเฉพาะอย่างยิ่งหากเนื้อหาใน slots ต่างๆ มีความแตกต่างกันอย่างมีนัยสำคัญ ตรวจสอบให้แน่ใจว่าเครื่องมือค้นหาสามารถ crawl และ index เนื้อหาได้อย่างถูกต้อง ใช้ canonical URLs อย่างเหมาะสม
- การดึงข้อมูล: จัดการการดึงข้อมูลอย่างรอบคอบ โดยเฉพาะเมื่อต้องจัดการกับส่วนต่างๆ ที่เป็นอิสระหลายส่วน พิจารณาใช้ data stores ที่ใช้ร่วมกันหรือกลไกการแคชเพื่อหลีกเลี่ยงการร้องขอข้อมูลที่ซ้ำซ้อน
- การเข้าถึง (Accessibility): ตรวจสอบให้แน่ใจว่าการใช้ parallel route ของคุณสามารถเข้าถึงได้โดยผู้ใช้ทุกคน รวมถึงผู้ที่มีความบกพร่อง ใช้ ARIA attributes และ HTML เชิงความหมายที่เหมาะสมเพื่อมอบประสบการณ์ผู้ใช้ที่ดี
การใช้งานขั้นสูง: Conditional Rendering และ Dynamic Slots
Parallel routes ไม่ได้จำกัดอยู่แค่การกำหนด slot แบบคงที่ คุณยังสามารถใช้ conditional rendering และ dynamic slots เพื่อสร้างเลย์เอาต์ที่ยืดหยุ่นยิ่งขึ้นได้
Conditional Rendering
คุณสามารถเรนเดอร์คอมโพเนนต์ต่างๆ ใน slot ตามเงื่อนไขได้ ขึ้นอยู่กับบทบาทของผู้ใช้ สถานะการยืนยันตัวตน หรือปัจจัยอื่นๆ
// app/product/[id]/layout.js
import { getUserRole } from '../../utils/auth';
export default async function ProductLayout({ children, cart }) {
const userRole = await getUserRole();
return (
{children}
);
}
function AdminPanel() {
return (
Admin Panel
Manage product details here.
);
}
ในตัวอย่างนี้ หากผู้ใช้มีบทบาทเป็น 'admin' คอมโพเนนต์ AdminPanel จะถูกเรนเดอร์ใน @cart slot แทนที่จะเป็นตะกร้าสินค้า
Dynamic Slots
แม้ว่าจะไม่ค่อยพบบ่อยนัก แต่ตามทฤษฎีแล้วคุณ *สามารถ* สร้างชื่อ slot แบบไดนามิกได้ แต่โดยทั่วไปไม่แนะนำเนื่องจากความซับซ้อนและผลกระทบที่อาจเกิดขึ้นกับประสิทธิภาพ การยึดตาม slots ที่กำหนดไว้ล่วงหน้าและเข้าใจได้ง่ายจะดีกว่า หากมีความจำเป็นต้องใช้ "slots" แบบไดนามิก ให้พิจารณาแนวทางแก้ไขอื่น เช่น การใช้คอมโพเนนต์ React มาตรฐานพร้อม props และ conditional rendering
ตัวอย่างการใช้งานจริงและกรณีศึกษา
ลองมาสำรวจตัวอย่างการใช้งาน parallel routes ในแอปพลิเคชันประเภทต่างๆ กัน:
- แพลตฟอร์มอีคอมเมิร์ซ: แสดงตะกร้าสินค้า คำแนะนำสินค้า หรือข้อมูลบัญชีผู้ใช้ควบคู่ไปกับหน้ารายละเอียดสินค้าหรือหน้าหมวดหมู่
- แดชบอร์ด: สร้างแดชบอร์ดที่มีวิดเจ็ตอิสระสำหรับแสดงเมตริก แผนภูมิ และรายงาน โดยแต่ละวิดเจ็ตสามารถโหลดและอัปเดตแยกกันได้โดยไม่ส่งผลกระทบต่อทั้งแดชบอร์ด แดชบอร์ดการขายอาจแสดงข้อมูลทางภูมิศาสตร์ใน parallel route หนึ่ง และประสิทธิภาพของสินค้าในอีก route หนึ่ง ทำให้ผู้ใช้สามารถปรับแต่งสิ่งที่เห็นได้โดยไม่ต้องโหลดหน้าใหม่ทั้งหมด
- แอปพลิเคชันโซเชียลมีเดีย: แสดงแถบด้านข้างสำหรับแชทหรือแผงการแจ้งเตือนควบคู่ไปกับฟีดหลักหรือหน้าโปรไฟล์
- ระบบจัดการเนื้อหา (CMS): แสดงหน้าต่างแสดงตัวอย่างหรือเครื่องมือแก้ไขควบคู่ไปกับเนื้อหาที่กำลังแก้ไข parallel route สามารถแสดงตัวอย่างสดของบทความที่กำลังเขียน ซึ่งอัปเดตแบบเรียลไทม์เมื่อมีการเปลี่ยนแปลง
- แพลตฟอร์มการเรียนรู้: แสดงเนื้อหาหลักสูตรควบคู่ไปกับการติดตามความคืบหน้าหรือฟีเจอร์การปฏิสัมพันธ์ทางสังคม
- แอปพลิเคชันทางการเงิน: แสดงราคาหุ้นแบบเรียลไทม์หรือสรุปพอร์ตโฟลิโอควบคู่ไปกับข่าวสารหรือบทวิเคราะห์ ลองนึกภาพเว็บไซต์ข่าวการเงินที่ใช้ parallel routes เพื่อแสดงข้อมูลตลาดสดควบคู่ไปกับข่าวด่วน ทำให้ผู้ใช้เห็นภาพรวมของภูมิทัศน์ทางการเงินได้อย่างครอบคลุม
- เครื่องมือการทำงานร่วมกันระดับโลก: ช่วยให้สามารถแก้ไขเอกสารหรือโค้ดพร้อมกันได้โดยมีแผงการประชุมทางวิดีโอหรือแชทแสดงผลอยู่ตลอดเวลา ทีมวิศวกรที่กระจายตัวอยู่ในซานฟรานซิสโก ลอนดอน และโตเกียว สามารถใช้ parallel routes เพื่อทำงานบนเอกสารการออกแบบเดียวกันแบบเรียลไทม์ โดยมีวิดีโอคอลแสดงผลอย่างถาวรในแถบด้านข้าง ส่งเสริมการทำงานร่วมกันอย่างราบรื่นข้ามเขตเวลา
สรุป
Next.js Parallel Routes เป็นฟีเจอร์ที่ทรงพลังซึ่งเปิดโลกแห่งความเป็นไปได้ใหม่ๆ สำหรับการสร้างเว็บแอปพลิเคชันที่ไดนามิกและยืดหยุ่น ด้วยการช่วยให้คุณสามารถเรนเดอร์ส่วนต่างๆ ที่เป็นอิสระหลายส่วนภายในเลย์เอาต์ของหน้าเดียวกันได้ parallel routes ทำให้คุณสามารถสร้างประสบการณ์ผู้ใช้ที่น่าดึงดูดยิ่งขึ้น เพิ่มความสามารถในการนำโค้ดกลับมาใช้ใหม่ และทำให้กระบวนการพัฒนาง่ายขึ้น แม้ว่าการพิจารณาความซับซ้อนที่อาจเกิดขึ้นและปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดจะเป็นสิ่งสำคัญ แต่การเชี่ยวชาญ parallel routes สามารถเพิ่มพูนทักษะการพัฒนา Next.js ของคุณได้อย่างมีนัยสำคัญ และช่วยให้คุณสร้างเว็บแอปพลิเคชันที่เป็นนวัตกรรมได้อย่างแท้จริง
ในขณะที่ Next.js ยังคงพัฒนาต่อไป Parallel Routes จะกลายเป็นเครื่องมือที่สำคัญมากขึ้นสำหรับนักพัฒนาที่ต้องการผลักดันขอบเขตของสิ่งที่เป็นไปได้บนเว็บอย่างไม่ต้องสงสัย ลองทดลองกับแนวคิดที่ได้อธิบายไว้ในคู่มือนี้ และค้นพบว่า Parallel Routes สามารถเปลี่ยนแปลงแนวทางการสร้างเว็บแอปพลิเคชันสมัยใหม่ของคุณได้อย่างไร